import json
import threading
from typing import Any

from entry.ICar import ICar
from store.IStoreService import IStoreService
from utils.CarHttp import CarHttp
from utils.CarUtil import CarUtil
from utils.LogUtil import LogUtil

_car: ICar = None
_mode: str = None
_http: CarHttp = None
_storeService: IStoreService = None
_logger: LogUtil = LogUtil


# 车型列表抓取任务单元
def ListPageCrawler():
  global _car, _mode, _http, _storeService, _logger

  # 1 首页获取所有的品牌列表页
  path = '/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x'
  res = _http.get(path)
  if not res or res.status_code != 200:
    _logger.warn(f'首页获取失败 [path: {_http.combineUrl(path)}]')
    return
  #endif

  # 通过 xpath 获取每个品牌的列表页
  xbrand_names = "//ul[contains(@class,'more-list')]/li/a//text()"
  xbrand_urls = "//ul[contains(@class,'more-list')]/li/a/@href"
  brand_list_name = CarUtil.extractByXPath(res.text, xbrand_names)
  brand_list_url = CarUtil.extractByXPath(res.text, xbrand_urls)
  brand_name_url = zip(brand_list_name, brand_list_url)

  if len(list(brand_name_url)) < 1:
    _logger.warn("没有获取到任何数据")
    return
  # 2 将每个品牌的列表页都装入缓存里
  for brand_name, brand_path in brand_name_url:
    # 添加品牌
    brand_id = CarUtil.extractByRe(brand_path, '\d+')
    data = ( brand_id, brand_name, brand_path )
    if _car.mode.upper() != "TEST":
      if _car.dataService.insert_brand(data) < 1:
        _logger.warn(
            f'已经添加过: [品牌:{brand_name} 路径:{_http.combineUrl(brand_path)}]'
        )

    # 3 添加品牌系列的id号
    res = _http.get(brand_path)
    if not res or res.status_code != 200:
      url = _http.combineUrl(brand_path)
      _logger.warn(f'品牌列表页获取失败 [url:{url}]')
      continue
    #endif

    # 4 获取某个品牌的车型页的 id 号
    xcar_info_name = "//li[contains(@class,'item-car-wrap')]//p[contains(@class,'car-name')]/text()"
    xcar_info_href = "//li[contains(@class,'item-car-wrap')]/a/@href"
    car_names = CarUtil.extractByXPath(res.text, xcar_info_name)
    car_hrefs = CarUtil.extractByXPath(res.text, xcar_info_href)
    car_list = list(zip(car_names, car_hrefs))

    for name, href in car_list:
      _logger.info(
          f'获取到: [品牌:{brand_name} 车名:{name} 路径:{_http.combineUrl(href)}]'
      )
      series_id = href.replace('/auto/series/', '')

      # 通过数据存储服务可以进行统一的数据存储
      if _mode != 'TEST':
        _storeService.insert_seriesId(series_id)
      #endif
    #endfor
  #endfor
  pass


# 新版
def ListPageCrawler2():
  global _car, _mode, _http, _storeService, _logger

  # 首页获取所有的品牌列表页
  url_path = '/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x'
  res = _http.get(url_path)
  if not res or res.status_code != 200:
    _logger.warn(f'首页获取失败 [url: {_http.combineUrl(url_path)}]')
    return
  #endif

  # 通过 正则 获取每个品牌的列表页
  jsonObj = getJsonObj(res.text)
  brands = None
  try:
    brands = jsonObj['props']['pageProps']['allBrands']['brand']
  except Exception as ex:
    LogUtil.error(ex, "没获取到任何品牌")

  for brand_item in brands:
    if brand_item['type'] == 1001:
      brand_info = brand_item['info']
      brand_id = brand_info['brand_id']
      brand_name = brand_info['brand_name']
      brand_path = f'/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-{brand_id}-x'
      insert_data = (
          brand_id,
          brand_item['unique_id'],
          brand_path,
          brand_name,
          brand_info['pinyin'],
          brand_info['business_status'],
          brand_info['image_url'],
          brand_info['on_sale_series_count'],
      )
      # 加上条件参数
      full_data = (*insert_data, brand_id)

      # 添加到数据库
      if _storeService.insert_brand(full_data) < 1:
        _logger.warn(
            f'已经添加过: [品牌:{brand_name} 路径:{_http.combineUrl(brand_path)}]'
        )
      else:
        _logger.info(f"[品牌:{brand_name}] 添加成功")

  # 添加品牌系列的id号
  for brand_item in brands:
    if brand_item['type'] == 1001:
      brand_info = brand_item['info']
      brand_id = brand_info['brand_id']

      # 获取品牌下的所有车型
      url_path = '/motor/pc/car/brand/select_series_v2'
      postdata = f'brand={brand_id}&sort_new=hot_desc&city_name=%E4%B9%9D%E6%B1%9F'
      res = _http.post(
          url_path,
          postdata,
          headers={ "Content-Type": "application/x-www-form-urlencoded"}
      )

      # 坚持品牌数据是否获取成功
      if not res or res.status_code != 200:
        _logger.warn(f'品牌列表页获取失败 [url:{_http.combineUrl(url_path)}]')
        continue

      jsonObj = res.json()
      if jsonObj and jsonObj['data']['series_count'] > 0:
        car_series = jsonObj['data']['series']
        for series_item in car_series:
          series_id = series_item['concern_id']
          dcar_score = series_item['dcar_score']
          brand_name = series_item['brand_name']
          outter_name = series_item['outter_name']
          u_path = f'/auto/series/{series_id}'
          # _logger.info(
          #     f'获取到: [品牌:{brand_name} 车名:{outter_name} 链接:{_http.combineUrl(u_path)}]'
          # )
          # 通过数据存储服务可以进行统一的数据存储
          if _mode != 'TEST':
            if _storeService.insert_seriesId(series_id, dcar_score):
              # 添加成功
              # _logger.info(
              #     f'添加成功: [品牌:{brand_name} 车名:{outter_name} 链接:{_http.combineUrl(url_path)}]'
              # )
              pass

  pass


@CarUtil.timmer
def start(car: ICar, *args, **kwargs):
  global _car, _mode, _http, _storeService, _logger

  _car = car
  _mode = car.mode
  _http = car.http
  _storeService = car.storeService

  if _mode == "PRODUCT":
    t = threading.Thread(target=ListPageCrawler2, daemon=True, args=( car, ))
    t.start()
  #endif
  elif _mode == "DEBUG":
    ListPageCrawler2()
  #endif
  elif _mode == "TEST":
    ListPageCrawler2()
  #endif


# 获取页面的json数据
def getJsonObj(text: str) -> Any | None:
  """获取页面的 json 数据

  Args:
      text (str): json 字符串文本

  Returns:
      Any | None: json 对象或为空
  """

  pattern = '<script id="__NEXT_DATA__" type="application/json" crossorigin="anonymous">(.*?)</script>'
  jsonStr = CarUtil.extractByRe(text, pattern, 1)
  jsonDict = json.loads(jsonStr)
  return jsonDict